package flexo.math;

/**
 * A 3D vector.
 */
public class Vector implements Comparable<Vector>
{
	private static final int NUM_TEMP_VECTORS = 300;
	private static Vector[] tempVector;
	private static int tempIterator;
	
	static
	{
		tempVector = new Vector[NUM_TEMP_VECTORS];
		for (int i = 0; i < NUM_TEMP_VECTORS; ++i)
		{
			tempVector[i] = new Vector();
		}
		tempIterator = 0;
	}
	
	public static Vector getTempVector()
	{
		if (tempIterator >= NUM_TEMP_VECTORS)
		{
			tempIterator = 0;
		}
		
		tempVector[tempIterator].reset();
		return tempVector[tempIterator++];
	}
	
	public float x,y,z;
	
	public Vector()
	{
		reset();
	}
	
	public void negate()
	{
		x = -x;
		y = -y;
		z = -z;
	}
	
	public void addScaledVector(Vector v, float scale)
	{
		this.x += v.x * scale;
		this.y += v.y * scale;
		this.z += v.z * scale;
	}
	
	protected float clamp(float value, float minValue, float maxValue)
	{
		if (x < minValue)
		{
			return minValue;
		}
		else if (x > maxValue)
		{
			return maxValue;
		}
		return value;
	}
	
	public void clamp(float minValue, float maxValue)
	{
		x = clamp(x,minValue,maxValue);
		y = clamp(y,minValue,maxValue);
		z = clamp(z,minValue,maxValue);
	}
	
	public void reset()
	{
		x = 0;
		y = 0;
		z = 0;
	}
	
	public Vector(float x, float y,float z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public Vector(double x, double y,double z)
	{
		this.x = (float)x;
		this.y = (float)y;
		this.z = (float)z;
	}
	
	public Vector(Vector copy)
	{
		x = copy.x;
		y = copy.y;
		z = copy.z;
	}
	
	public void set(Vector v)
	{
		set(v.x,v.y,v.z);
	}
	
	public void set(float x, float y, float z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	public void set(Vector v, float scale)
	{
		set(v);
		scale(scale);
	}
	
	public void scale(float value)
	{
		x *= value;
		y *= value;
		z *= value;
	}
	
	public void move(Vector step)
	{
		move(step.x,step.y,step.z);
	}
	
	public void move(float x, float y, float z)
	{
		this.x += x;
		this.y += y;
		this.z += z;
	}
	
	public float getLengthSq()
	{
		return (x * x) + (y * y) + (z * z);
	}
	
	public float getLength()
	{
		return (float)Math.sqrt(getLengthSq());
	}
	
	public void normalize()
	{
		
		float root = 1.0f / getLength();
		x *= root;
		y *= root;
		z *= root;
	}
	
	public static Vector add(Vector op1, Vector op2)
	{
		Vector result = new Vector();
		add(op1,op2,result);
		return result;
	}
	
	public static void add(Vector op1, Vector op2, Vector result)
	{
		result.x = op1.x + op2.x;
		result.y = op1.y + op2.y;
		result.z = op1.z + op2.z;
	}
	
	public static void sub(Vector op1, Vector op2, Vector result)
	{
		result.x = op1.x - op2.x;
		result.y = op1.y - op2.y;
		result.z = op1.z - op2.z;
	}
	
	public static void mul(Vector op1, Vector op2, Vector result)
	{
		result.x = op1.x * op2.x;
		result.y = op1.y * op2.y;
		result.z = op1.z * op2.z;
	}
	
	public static void div(Vector op1, Vector op2, Vector result)
	{
		result.x = op1.x / op2.x;
		result.y = op1.y / op2.y;
		result.z = op1.z / op2.z;
	}
	
	public String toString()
	{
		return "x:" + x + ", y:" + y + ", z:" + z;
	}
	
	public static void cross(Vector op1, Vector op2, Vector result)
	{
		result.x = op1.y * op2.z - op1.z * op2.y;
		result.y = op1.z * op2.x - op1.x * op2.z;
		result.z = op1.x * op2.y - op1.y * op2.x;
	}
	
	public static Vector cross(Vector op1, Vector op2)
	{
		Vector result = new Vector();
		result.x = op1.y * op2.z - op1.z * op2.y;
		result.y = op1.z * op2.x - op1.x * op2.z;
		result.z = op1.x * op2.y - op1.y * op2.x;
		return result;
	}
	
	public float dot(Vector op)
	{
		return Vector.dot(this, op);
	}
	
	public static float dot(Vector op1, Vector op2)
	{
		return op1.x * op2.x + op1.y * op2.y + op1.z * op2.z;
	}
	
	public boolean equals(Vector v)
	{
		if (compareTo(v) == 0)
		{
			return true;
		}
		return false;
	}

	@Override
	public int compareTo(Vector v)
	{
		if (x == v.x && y == v.y && z == v.z)
		{
			return 0;
		}
		return 1;
	}
}
